package org.kca.yarmour.data;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;

import org.kca.yarmour.data.GlobalData.*;

public class Player
{
    /* Attributes */
    private int currentHull, troopers;
    
    /* Possessions */
    private int money;
    private int[] cargo, reputation;
    private VehicleStats vehicle;
    private List<InvItem> inventory;
    
    /* Temporal State */
    private int row, col;
    private int facing;
    
    private List<Quest> activeQuests, completedQuests;
    private HashMap<String, Integer> questRecord;
    private HashSet<String> flags;
    
    public Player()
    {
        money = 46000;
        cargo = new int[CargoType.getNumTypes()];
        reputation = new int[FactionType.getNumFactions()];
        inventory = new ArrayList<InvItem>();
        activeQuests = new ArrayList<Quest>();
        completedQuests = new ArrayList<Quest>();
        questRecord = new HashMap<String, Integer>();
        flags = new HashSet<String>();
    }

    /**
     * Gets the player's row position on the map
     * @return the row
     */
    public int getRow()
    {
        return row;
    }

    /**
     * Sets the player's row position on the map
     * @param row the row to set
     */
    public void setRow(int row)
    {
        this.row = row;
    }

    /**
     * Gets the player's column position on the map
     * @return the col
     */
    public int getCol()
    {
        return col;
    }

    /**
     * Sets the player's column position on the map
     * @param col the col to set
     */
    public void setCol(int col)
    {
        this.col = col;
    }

    /**
     * Gets the coordinates of the player as a displayable string
     * @return the coordinates of the player
     */
    public String getCoordString()
    {
        return "(" + (col + 1) + "," + (row + 1) + ")";
    }

    /**
     * Gets the player's facing direction on the world map
     * @return the facing direction
     */
    public int getFacing()
    {
        return facing;
    }

    /**
     * Sets the player's facing direction 
     * @param facing the facing direction to set
     */
    public void setFacing(int facing)
    {
        this.facing = facing;
    }

    /**
     * Gets the vehicle's current hull durability (ie. HP)
     * @return the current hull
     */
    public int getCurrentHull()
    {
        return currentHull;
    }

    /**
     * Sets the vehicle's current hull durability (ie. HP)
     * @param currentHull the current hull durability to set
     */
    public void setCurrentHull(int currentHull)
    {
        this.currentHull = currentHull;
    }
    
    /**
     * Restores the current hull value to the max value
     */
    public void restoreHull()
    {
        setCurrentHull(getMaxHull());
    }

    /**
     * Gets the max hull durability for this vehicle
     * @return the vehicle's max hull
     */
    public int getMaxHull()
    {
        if (vehicle != null)
        {
            return vehicle.getMaxHull();
        }
        else return 0;
    }
    
    /**
     * Gets the inventory
     * @return the inventory
     */
    public List<InvItem> getInventory()
    {
        return inventory;
    }
    
    /**
     * Checks if the player has at least a specified amount of the item with specified id
     * TODO: Linear search currently being used. Can be improved if necessary
     * @param id the item id to search for
     * @return true if the player has at least that number of that item
     */
    public boolean hasItem(String id, int amount)
    {
        int count = 0;
        for (InvItem item : inventory)
        {
            if (item.getId().equals(id))
            {
                count++;
            }
        }
        return count >= amount;
    }
    
    /**
     * Gets the vehicle's current amount of cargo
     * @return the currentCargo the current cargo
     */
    public int getCurrentCargo()
    {
        int sum = 0;
        for (int i = 0; i < cargo.length; i++)
        {
            sum += cargo[i];
        }
        for (InvItem item : inventory)
        {
            sum += item.getSpace();
        }
        return sum;
    }
    
    /**
     * Gets the amount of a particular cargo
     * @param index the index of that cargo type
     * @return the amount of that cargo
     */
    public int getCargo(int index)
    {
        return cargo[index];
    }
    
    /**
     * Sets the amount of a particular cargo. 
     * @param index the index of that cargo type
     * @param amount the amount of that cargo
     */
    public void setCargo(int index, int amount)
    {
        cargo[index] = amount;
    }
    
    /**
     * Gets the reputation with a particular faction
     * @param index the faction index
     * @return the reputation with that faction
     */
    public int getReputation(int index)
    {
        return reputation[index];
    }
    
    /**
     * Sets the reputation with a particular faction
     * @param index the faction index
     * @param amount the reputation with that faction
     */
    public void setReputation(int index, int amount)
    {
        reputation[index] = amount;
    }
    
    /**
     * Gets the vehicle's max cargo space
     * @return the max space
     */
    public int getMaxCargo()
    {
        if (vehicle != null)
        {
            return vehicle.getMaxSpace();
        }
        else return 0;
    }
    
    /**
     * Gets the number of troopers
     * @return the number of troopers
     */
    public int getTroopers()
    {
        return troopers;
    }

    /**
     * Sets the number of troopers
     * @param troopers the number of troopers to set
     */
    public void setTroopers(int troopers)
    {
        this.troopers = troopers;
    }

    /**
     * Gets the current money owned
     * @return the money owned
     */
    public int getMoney()
    {
        return money;
    }

    /**
     * Sets the current money owned
     * @param money the amount of money to set
     */
    public void setMoney(int money)
    {
        this.money = money;
    }

    /**
     * Gets the current vehicle's stats
     * @return the current vehicle stats
     */
    public VehicleStats getVehicle()
    {
        return vehicle;
    }

    /**
     * Sets the current vehicle's stats
     * @param vehicle the vehicle to set
     */
    public void setVehicle(VehicleStats vehicle)
    {
        this.vehicle = vehicle;
    }
    
    /**
     * Gets the number of times the quest with the specified id has been completed
     * @param questId the quest id
     * @return the number of times the quest has been completed
     */
    public int getQuestCompletionCount(String questId)
    {
        if (questRecord.containsKey(questId))
        {
            return questRecord.get(questId);
        }
        return 0;
    }

    /**
     * Gets the activeQuests
     * @return the activeQuests
     */
    public List<Quest> getActiveQuests()
    {
        return activeQuests;
    }

    /**
     * Gets the completedQuests
     * @return the completedQuests
     */
    public List<Quest> getCompletedQuests()
    {
        return completedQuests;
    }

    /**
     * Sets a flag. Flags cannot be unset
     * @param flag the flag to set
     */
    public void setFlag(String flag)
    {
        flags.add(flag);
    }
    
    /**
     * Checks if a flag has been set
     * @param flag the flag to check
     * @return true if the flag is set
     */
    public boolean checkFlag(String flag)
    {
        return flags.contains(flag);
    }

    /* Advanced methods */
    /**
     * Buys from the supermarket in the specified town
     * @param town the town containing the supermarket to buy from
     * @param buyQty the amount to buy expressed as an array of each cargo type
     * @return how much of each cargo type is actually bought, expressed as an array
     */
    public int[] buyFromSupermarket(Town town, int[] buyQty)
    {
        int[] res = new int[CargoType.getNumTypes()];
        for (int i = 0; i < CargoType.getNumTypes(); i++)
        {
            if (buyQty[i] < 0)
            {
                res[i] = 0;
                continue;
            }
            int bq = Math.min(buyQty[i], town.getSupply(i)); 
            bq = Math.min(bq, getMaxCargo() - getCurrentCargo());
            bq = Math.min(bq, money / town.getBuyPrice(i));
            res[i] = bq;
            setCargo(i, bq + getCargo(i));
            town.setSupply(i, town.getSupply(i) - bq);
            money -= bq * town.getBuyPrice(i);
        }
        return res;
    }
    
    /**
     * Sells from the supermarket in the specified town
     * @param town the town containing the supermarket to sell to
     * @param sellQty the amount to sell expressed as an array of each cargo type
     * @return how much of each cargo type is actually sold, expressed as an array
     */
    public int[] sellToSupermarket(Town town, int[] sellQty)
    {
        int[] res = new int[CargoType.getNumTypes()];
        for (int i = 0; i < CargoType.getNumTypes(); i++)
        {
            if (sellQty[i] <= 0 || sellQty[i] > getCargo(i))
            {
                res[i] = 0;
                continue;
            }
            res[i] = sellQty[i];
            setCargo(i, getCargo(i) - sellQty[i]);
            town.setSupply(i, town.getSupply(i) + sellQty[i]);
            money += sellQty[i] * town.getSellPrice(i);
        }
        return res;
    }
}
